using System;
using System.IO;
using System.Net;
using System.Web;
using System.Text;
using System.Windows.Forms;
using SecureCredentialsLibrary;

namespace FPSEPublish
{
	/// <summary>
	/// Summary description for FPSEPublish.
	/// </summary>
	public class FPSEPublish
	{
        private NetworkCredential currentNetworkCredential;
        bool cachedUrls;
        private string cachedWebUrl;
        private string cachedServer;

		public FPSEPublish()
		{
            cachedUrls = false;
            currentNetworkCredential = CredentialCache.DefaultNetworkCredentials;
		}

        public void InvalidateCache()
        {
            cachedUrls = false;
        }

        public void CacheSPSiteUrl(string SiteUrl)
        {
            // invalidate the cache
            cachedUrls = false;

            // get the weburl
            string webUrl;
            string fileUrl;
            UrlToWebUrl(SiteUrl, out webUrl, out fileUrl);

            // get the cache values
            Uri uri = new Uri(SiteUrl);
            cachedServer = uri.GetLeftPart(UriPartial.Authority);
            cachedWebUrl = webUrl;

            // validate the cache
            cachedUrls = true;
        }
		
		public void UrlToWebUrl(string uri, out string webUrl, out string fileUrl)
		{
            if (!cachedUrls)
            {
                Uri myUri = new Uri(uri);

                string postBody = String.Format("method=url+to+web+url&url={0}&flags=0", myUri.AbsolutePath);
                string response = SendRequest(myUri.GetLeftPart(UriPartial.Authority) + "/_vti_bin/shtml.dll/_vti_rpc", postBody);

                webUrl = GetReturnValue(ref response, "webUrl");
                fileUrl = GetReturnValue(ref response, "fileUrl");
            }
            else
            {
                webUrl = cachedWebUrl;
                fileUrl = HttpUtility.UrlDecode(uri.Substring(cachedServer.Length + cachedWebUrl.Length));
                
                // trim / at beginning of file URL if its there
                if (fileUrl.StartsWith("/"))
                {
                    fileUrl = fileUrl.Substring(1);
                }
            }
		}

		public void PutDocument(string uri, string fileName, string metaInfo)
		{
			Uri myUri = new Uri(uri);
			string webUrl, fileUrl;

            UrlToWebUrl(uri, out webUrl, out fileUrl);		

			if (null == metaInfo)
				metaInfo = "";

			if (!File.Exists(fileName))
				throw new Exception("Could not find file" + fileName);

			string postBody = String.Format(
				"method=put+document&service_name=&document=[document_name={0};meta_info=[{1}]]&put_option=overwrite&comment=&keep_checked_out=false\n",
				HttpUtility.UrlEncode(fileUrl),
				metaInfo);

			ASCIIEncoding encoding = new ASCIIEncoding();
			MemoryStream stream = new MemoryStream();
			stream.Write(encoding.GetBytes(postBody), 0, postBody.Length);

			FileStream fs = File.OpenRead(fileName);
			byte[] b = new byte[4096];
            int len = fs.Read(b, 0, b.Length);
			while (len > 0)
			{
				stream.Write(b, 0, len);
                len = fs.Read(b, 0, b.Length);
			}
			fs.Close();

			SendRequest(myUri.GetLeftPart(UriPartial.Authority) + webUrl + "/_vti_bin/_vti_aut/author.dll", stream.GetBuffer(), stream.Length);
			stream.Close();
		}

		public void CheckinDocument(string uri)
		{
			Uri myUri = new Uri(uri);
			string webUrl, fileUrl;
			UrlToWebUrl(uri, out webUrl, out fileUrl);

			string postBody = String.Format("method=checkin+document&service_name=&document_name={0}&comment=&keep_checked_out=false", fileUrl);
			SendRequest(myUri.GetLeftPart(UriPartial.Authority) + webUrl + "/_vti_bin/_vti_aut/author.dll", postBody);
		}

		public void SetDocsMetaInfo(string uri, string metaInfo)
		{
			Uri myUri = new Uri(uri);
			string webUrl, fileUrl;
			UrlToWebUrl(uri, out webUrl, out fileUrl);

			string postBody = String.Format(
				"method=set+document+meta-info&service_name=&document_name={0}&meta_info=[{1}]",
				fileUrl,
				metaInfo);
			SendRequest(myUri.GetLeftPart(UriPartial.Authority) + webUrl + "/_vti_bin/_vti_aut/author.dll", postBody);
		}

        public void CreateFolder(string uri)
        {
            Uri myUri = new Uri(uri);
            string webUrl, fileUrl;
            UrlToWebUrl(uri, out webUrl, out fileUrl);

            string postBody = String.Format("method=create+url-directory&service_name=&url={0}", fileUrl);
            SendRequest(myUri.GetLeftPart(UriPartial.Authority) + webUrl + "/_vti_bin/_vti_aut/author.dll", postBody);
        }

        public void RemoveFileOrFolder(string uri)
        {
            Uri myUri = new Uri(uri);
            string webUrl, fileUrl;
            UrlToWebUrl(uri, out webUrl, out fileUrl);

            string postBody = String.Format("method=remove+documents&service_name=&url_list=[{0}]", fileUrl);
            SendRequest(myUri.GetLeftPart(UriPartial.Authority) + webUrl + "/_vti_bin/_vti_aut/author.dll", postBody);
        }

		private string SendRequest(string uri, byte[] postBody, long postLength)
		{
			string responseText = null;

            Uri target = new Uri(uri);
            CredentialsDialog dialog = new CredentialsDialog(target.Host, "Connect to " + target.Host, "The server " + target.Host + " requires a username and password.");

            //wrap in a while loop as we may do this multiple times            
            bool useCredDialog = false;
            bool retry = true;
            bool success = true;

            while (retry)
            {                
				WebRequest request = WebRequest.Create(uri);
                
				request.Method = "POST";
				request.ContentType = "application/x-www-form-urlencoded";
				request.Headers.Add("X-Vermeer-Content-Type", "application/x-www-form-urlencoded");
				Stream newStream = request.GetRequestStream();

				int offset = 0;
				while(postLength > 0)
				{
					if (postLength > 4096)
					{
						newStream.Write(postBody, offset, 4096);
						postLength -= 4096;
						offset += 4096;
					}
					else
					{
						newStream.Write(postBody, offset, Convert.ToInt32(postLength));
						break;
					}
				}
                newStream.Close();

                // set the credentials for this request
                NetworkCredential credential;
                if (useCredDialog)
                {
                    // Get credentials from dialog    
                    string domain = "";
                    string name = dialog.Name;
                    string password = dialog.Password;
                    
                    // parse domain from username if provided
                    int pos = name.LastIndexOf('\\');
                    if (pos != -1)
                    {
                        domain = name.Substring(0, pos);
                        name = name.Substring(pos + 1, name.Length - (pos + 1));
                    }

                    // parse domain from uri
                    else
                    {
                        domain = target.GetComponents(UriComponents.Host,UriFormat.Unescaped);
                    }

                    credential = new NetworkCredential(name,password,domain);
                }
                else
                {
                    credential = currentNetworkCredential;
                }
                request.Credentials = credential;

                try
                {
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    
                    Stream receiveStream = response.GetResponseStream();
                    StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);

                    responseText = readStream.ReadToEnd();

                    response.Close();
                    readStream.Close();

                    // ASSERT: That we got a response to our request

                    // since we got a response, persist credentials
                    {
                        if (dialog.SaveChecked) dialog.Confirm(true);
                        currentNetworkCredential = credential;
                    }
                    
                    // Check if the request succeeded
                    if (response.StatusCode != HttpStatusCode.OK)
                    {
                        success = false;
                        responseText = response.StatusCode + ": " + responseText;
                    }

                    // set flag to exite retry loop
                    retry = false;
                }
                // request failed
                catch (WebException e)
                {    
                    // handle unauthorized case
                                        
                    if (e.Status == WebExceptionStatus.ProtocolError)
                    {            
                        // pop the permissions dialog
                        if (dialog.Show() == DialogResult.OK && dialog.Name != "")
                        {
                            useCredDialog = true;
                        }
                        else
                        {
                            retry = false;
                        }
                    }
                    // handle other exceptions
                    else
                    {
                        //Console.WriteLine("ERROR: {0}", e.Message);
                        retry = false;
                        throw e;
                    }
                }
			}

            if (success == false)
            {
                Exception e = new Exception(responseText);
                throw e;
            }

			return responseText;
		}

		private string SendRequest(string uri, string postBody)
		{
			ASCIIEncoding encoding = new ASCIIEncoding();
			return SendRequest(uri, encoding.GetBytes(postBody), postBody.Length);
		}

		private string GetReturnValue(ref string response, string key)
		{
			int start = response.IndexOf(key + "=");
			if (-1 == start)
				return null;
			else
				start += key.Length + 1;

			int end = response.IndexOf("\n", start);
			return response.Substring(start, end - start);
		}

	}
}
